/*  
  需求：编写客户端，发送两个整型变量作为请求数据，并处理响应结果。
  步骤：
    1.包含头文件；
    2.初始化 ROS2 客户端；
    3.定义节点类；
      3-1.创建客户端；
      3-2.等待服务连接；
      3-3.组织请求数据并发送；
    4.创建对象指针调用其功能,并处理响应；
    5.释放资源。

*/

// 1.包含头文件；
#include"rclcpp/rclcpp.hpp"
#include"base_interfaces_demo/srv/add_ints.hpp"

using base_interfaces_demo::srv::AddInts;
using namespace std::chrono_literals;

// 3.定义节点类；

class Client:public rclcpp::Node
{
public:
    Client():Node("client_node_cpp")
    {
        //3-1.创建客户端；
        client_ = this->create_client<AddInts>("add_ints");
        RCLCPP_INFO(this->get_logger(),"客户端创建，等待连接服务端！");
        
    }
    //3-2.等待服务连接；
    bool connect_server()
    {
        //在指定超时时间内连接服务器client_->wait_for_service()
        //利用while循环，只要连接不上就一直等待
        while(!client_->wait_for_service(1s)) //循环等待服务器连接
        {
            //对crtl+c进行判断，做特殊处理,crtl+c按下表明节点关闭
            if(!rclcpp::ok())
            {   
                //不使用this->的get_logger,是因为日志管理器会与context有关，init初始化时会创建context对象
                //按下crtl+会释放资源，关闭context容器，便不能再从中获取日志资源
                RCLCPP_INFO(rclcpp::get_logger("rclcpp"),"强制退出！");
                return false;
            }
             RCLCPP_INFO(this->get_logger(),"服务连接中，请稍候...");
        }
        return true;
    }

    //     3-3.组织请求数据并发送；
    //编写发送请求函数--参数是两个整形数据，返回值是服务端返回结果
    // rclcpp::Client<AddInts>::FutureAndRequestId send_request(int32_t num1, int32_t num2)
    // {
    //foxy版本需要用到SharedFuture 才能匹配 client_->async_send_request(request)异步发送请求函数
    rclcpp::Client<base_interfaces_demo::srv::AddInts>::SharedFuture send_request(int num1, int num2)
    //rclcpp::Client<AddInts>::SharedFutureWithRequest send_request(int num1, int num2)
    {
        //组织请求数据
        
        //发送
        /*
            rclcpp::Client<ServiceT>::async_send_request()
            [其中 ServiceT=base_interfaces_demo::srv::AddInts
            对象类型是 std::make_shared<AddInts::Request>() 智能指针

            std::shared_future<std::shared_ptr<base_interfaces_demo::srv::AddInts_Response>> 
            rclcpp::Client<base_interfaces_demo::srv::AddInts>::async_send_request(std::shared_ptr<base_interfaces_demo::srv::AddInts_Request> request)
        */
        auto request = std::make_shared<AddInts::Request>();
        request->num1 = num1;
        request->num2 = num2;
        return client_->async_send_request(request);

    }

private:
    
    // }
    rclcpp::Client<AddInts>::SharedPtr client_;
    
};

int main(int argc, char const *argv[])
{
    if(argc != 3) //argc为输入参数的数量，其中第一个为固定值占位符,argv[0]
    {
        
        RCLCPP_ERROR(rclcpp::get_logger("rclcpp"),"请提交两个整型数据！");
        return 1;
    }
    // 2.初始化 ROS2 客户端；
    rclcpp::init(argc,argv);

    // 4.创建对象指针调用其功能,并处理响应；
    auto client = std::make_shared<Client>();
    //调用客户端对象连接服务器功能
    bool flag = client->connect_server();
    //根据结果做进一步的处理
    if(!flag)
    {
        RCLCPP_INFO(rclcpp::get_logger("rclcpp"),"服务连接失败！");
        return 0;
    }

    //调用请求提交函数，接受并处理响应结果
    //auto future = client->send_request(10,20);//操作不灵活
    //atoi()转换一个字符串数据到整型数据，根据命令行输入参数进行调整
    auto future = client->send_request(atoi(argv[1]),atoi(argv[2]));

    //处理响应
    //利用future获取状态码，根据三种状态进行判断
    if(rclcpp::spin_until_future_complete(client,future) == rclcpp::FutureReturnCode::SUCCESS) //成功
    {
        RCLCPP_INFO(client->get_logger(),"响应成功!sum = %d",future.get()->sum);
    }
    else //失败
    {
        RCLCPP_INFO(client->get_logger(),"响应失败!");
    }
    // 5.释放资源。
    rclcpp::shutdown();
    return 0;
}
